package com.pai4j.pgc.service.resource;
import com.google.common.collect.Lists;
import com.pai4j.common.bean.PAIPageResponseBeanUtil;
import com.pai4j.common.constants.MessageConstants;
import com.pai4j.common.enums.CommonStatusEnum;
import com.pai4j.common.enums.community.*;
import com.pai4j.common.exception.AuthException;
import com.pai4j.common.exception.BusinessException;
import com.pai4j.common.exception.NotFoundException;
import com.pai4j.common.service.FileUploadService;
import com.pai4j.common.util.IDGenerateUtil;
import com.pai4j.common.util.JsonUtil;
import com.pai4j.common.util.ThreadPoolExecutorUtil;
import com.pai4j.common.util.TransactionSyncManagerUtil;
import com.pai4j.domain.community.vo.request.resource.ResourceReviewRequestVO;
import com.pai4j.domain.community.vo.request.resource.ResourceSaveOrUpdateVO;
import com.pai4j.domain.community.vo.response.admin.DataCountResponseVO;
import com.pai4j.domain.community.vo.response.message.MessageResourceVO;
import com.pai4j.domain.community.vo.response.resource.*;
import com.pai4j.domain.dto.message.SystemMessageDTO;
import com.pai4j.domain.vo.response.ResourceBaseInfoVO;
import com.pai4j.domain.vo.response.UserBaseResponseInfoVO;
import com.pai4j.pgc.dao.community.ICCVideoDAO;
import com.pai4j.pgc.dao.community.IResourceDAO;
import com.pai4j.pgc.dao.community.IResourceDownloadDAO;
import com.pai4j.pgc.entity.community.ResourceBehaviorEntity;
import com.pai4j.pgc.entity.community.ResourceDownloadEntity;
import com.pai4j.pgc.entity.community.ResourceEntity;
import com.pai4j.pgc.pojo.convert.EsSyncResourceConvert;
import com.pai4j.pgc.pojo.convert.ResourceEntityConvert;
import com.pai4j.pgc.pojo.es.DtoRequest;
import com.pai4j.pgc.service.behavior.ResourceBehaviorFactory;
import com.pai4j.pgc.service.community.CCPathService;
import com.pai4j.pgc.service.behavior.AbstractResourceBehaviorService;
import com.pai4j.pgc.service.community.CommonInterface;
import com.pai4j.pgc.service.community.ResourceMessageInterface;
import com.pai4j.pgc.service.message.builder.SystemMessageBuilder;
import com.pai4j.pgc.service.permission.RoleService;
import com.pai4j.pgc.service.queue.UserMessageQueue;
import com.pai4j.pgc.service.resource.convert.AbstractResourceConvertImageService;
import com.pai4j.pgc.service.resource.convert.ResourceConvertImageFactory;
import com.pai4j.remote.user.UserServiceClient;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 *
 * @author: CYM-pai
 * @date: 2025/10/31 11:29
 **/
@Slf4j
@Service
public class ResourceService implements CommonInterface, ResourceMessageInterface {

    @Autowired
    private IResourceDAO resourceDAO;
    @Autowired
    private IResourceDownloadDAO resourceDownloadDAO;
    @Autowired
    private ResourceCacheService resourceCacheService;
    @Autowired
    private ResourceListService resourceListService;
    @Autowired
    private ResourceSearchService resourceSearchService;
    @Autowired
    private ResourceRecommendService resourceRecommendService;
    @Autowired
    private UserServiceClient userServiceClient;
    @Autowired
    private FileUploadService fileUploadService;
    @Autowired
    private ResourceDownloadRecordService resourceDownloadRecordService;
    @Autowired
    private UserMessageQueue messageQueue;
    @Autowired
    private RoleService roleService;
    @Autowired
    private ICCVideoDAO videoDAO;
    @Autowired
    private CCPathService pathService;

    /**
     * 最大转换10页
     */
    private static final int MAX_CONVERT_IMAGE_COUNT = 10;

    /***
     * 提交资源
     * @param resourceSaveOrUpdateVO
     * @return
     */
    @Transactional
    public String submit(ResourceSaveOrUpdateVO resourceSaveOrUpdateVO) {
        // 是否首次创建
        final boolean isFirstSubmit = StringUtils.isBlank(resourceSaveOrUpdateVO.getResourceId());
        /***
         * 前置执行
         * 用于资源数据校验、用户权限校验、首次创建、内容更新
         */
        ResourceEntity resourceEntity = this.submitBeforeProcess(resourceSaveOrUpdateVO);
        /***
         * 后置执行 (因为要依赖最终的提交结果，同时后置处理不需要实时进行，所以这里选择事务执行后异步处理) 资源转图片、消息、缓存刷新
         */
        TransactionSyncManagerUtil.registerSynchronization(() -> this.submitAfterProcess(isFirstSubmit, resourceEntity));
        /***
         * 提交成功返回资源业务ID
         */
        log.info("{}成功{}了瑞索斯资源:{}", resourceEntity.getAuthor(), isFirstSubmit ? "提交" : "更新",
                JsonUtil.toJsonString(resourceSaveOrUpdateVO));
        return resourceEntity.getResourceId();
    }

    /***
     * 资源审核
     * @param resourceReviewRequestVO
     */
    @Transactional
    public void review(ResourceReviewRequestVO resourceReviewRequestVO) {
        // 校验审核状态是否正确
        ReviewStatusEnum.validateStatus(resourceReviewRequestVO.getReviewStatus());
        ResourceEntity resourceEntity = resourceDAO.findAllByResourceId(resourceReviewRequestVO.getResourceId());
        Assert.isTrue(resourceEntity != null &&
                !CommonStatusEnum.DELETED.getStatusCode().equals(resourceEntity.getStatus()), "资源不存在或已被删除");
        resourceEntity.setReviewUserId(resourceReviewRequestVO.getOperator());
        resourceEntity.setReviewStatus(resourceReviewRequestVO.getReviewStatus());
        resourceEntity.setReviewReason(resourceReviewRequestVO.getReviewReason());
        resourceEntity.setReviewDate(new Date());
        resourceDAO.save(resourceEntity);
        // 刷新资源缓存
        TransactionSyncManagerUtil.registerSynchronization(() -> this.reviewAfterProcess(resourceEntity));
    }


    public void delete(String userId, String resourceId) {
        ResourceEntity resourceEntity = resourceDAO.findAllByResourceId(resourceId);
        Assert.isTrue(resourceEntity != null &&
                !CommonStatusEnum.DELETED.getStatusCode().equals(resourceEntity.getStatus()), "资源不存在或已被删除");
        if (!(resourceEntity.getAuthor().equals(userId) || userServiceClient.isAdmin(userId))) {
            throw new AuthException("没有删除权限");
        }
        resourceEntity.setStatus(CommonStatusEnum.DELETED.getStatusCode());
        resourceDAO.save(resourceEntity);
        // 刷新资源缓存
        ThreadPoolExecutorUtil.execute(() -> resourceCacheService.refreshResourceDetailCacheByResourceEntity(resourceEntity));
    }

    /***
     * 同步首页
     * @param userId
     * @param resourceId
     */
    public void syncHome(String userId, String resourceId) {
        ResourceEntity resourceEntity = resourceDAO.findAllByResourceId(resourceId);
        Assert.isTrue(resourceEntity != null &&
                !CommonStatusEnum.DELETED.getStatusCode().equals(resourceEntity.getStatus()), "资源不存在或已被删除");
        resourceEntity.setIsSyncHome(true);
        resourceDAO.save(resourceEntity);
        // 刷新资源缓存
        ThreadPoolExecutorUtil.execute(() -> {
            resourceCacheService.refreshResourceDetailCacheByResourceEntity(resourceEntity);
            resourceListService.refreshSyncHomeListCache(resourceEntity.getPath());
            if (resourceEntity.getCcId() != null) {
                resourceListService.refreshSyncCTCHomeListCache(resourceEntity.getCcId());
            }
            // 即时消息
            UserBaseResponseInfoVO user = userServiceClient.getUserBaseInfoByUserId(resourceEntity.getAuthor());
        });
        log.info("{}同步资源{}到首页", userId, resourceEntity);
    }

    /***
     * 取消同步首页
     * @param userId
     * @param resourceId
     */
    public void cancelSyncHome(String userId, String resourceId) {
        ResourceEntity resourceEntity = resourceDAO.findAllByResourceId(resourceId);
        Assert.isTrue(resourceEntity != null &&
                !CommonStatusEnum.DELETED.getStatusCode().equals(resourceEntity.getStatus()), "资源不存在或已被删除");
        resourceEntity.setIsSyncHome(false);
        resourceDAO.save(resourceEntity);
        // 刷新资源缓存
        ThreadPoolExecutorUtil.execute(() -> {
            resourceCacheService.refreshResourceDetailCacheByResourceEntity(resourceEntity);
            resourceListService.refreshSyncHomeListCache(resourceEntity.getPath());
            if (resourceEntity.getCcId() != null) {
                resourceListService.refreshSyncCTCHomeListCache(resourceEntity.getCcId());
            }
        });
        log.info("{}取消资源{}到首页", userId, resourceEntity);
    }

    /***
     * 精品
     * @param userId
     * @param resourceId
     */
    public void boutique(String userId, String resourceId) {
        ResourceEntity resourceEntity = resourceDAO.findAllByResourceId(resourceId);
        Assert.isTrue(resourceEntity != null &&
                !CommonStatusEnum.DELETED.getStatusCode().equals(resourceEntity.getStatus()), "资源不存在或已被删除");
        Assert.isTrue(ResourcePayTypeEnum.isMoney(resourceEntity.getPayType()), "非付费类资源");
        resourceEntity.setIsBoutique(true);
        resourceDAO.save(resourceEntity);
        // 刷新资源缓存
        ThreadPoolExecutorUtil.execute(() -> {
            resourceCacheService.refreshResourceDetailCacheByResourceEntity(resourceEntity);
            resourceListService.refreshBoutiqueListCache();
        });
        log.info("{}加精了资源{}", userId, resourceEntity);
    }

    /***
     * 取消精品
     * @param userId
     * @param resourceId
     */
    public void cancelBoutique(String userId, String resourceId) {
        ResourceEntity resourceEntity = resourceDAO.findAllByResourceId(resourceId);
        Assert.isTrue(resourceEntity != null &&
                !CommonStatusEnum.DELETED.getStatusCode().equals(resourceEntity.getStatus()), "资源不存在或已被删除");
        Assert.isTrue(ResourcePayTypeEnum.isMoney(resourceEntity.getPayType()), "非付费类资源");
        resourceEntity.setIsBoutique(false);
        resourceDAO.save(resourceEntity);
        // 刷新资源缓存
        ThreadPoolExecutorUtil.execute(() -> {
            resourceCacheService.refreshResourceDetailCacheByResourceEntity(resourceEntity);
            resourceListService.refreshBoutiqueListCache();
        });
        log.info("{}取消加精了资源{}", userId, resourceEntity);
    }

    /***
     * 修改资源为付费下载资源
     * @param userId 用户ID
     * @param resourceId 资源
     * @param amount 金额
     */
    @Transactional
    public void changePayMoneyAmount(String userId, String resourceId, Integer amount) {
        updatePayType(userId, resourceId, ResourcePayTypeEnum.MONEY.getType(), amount);
        log.info("{} 修改资源{}为付费下载资源，下载金额：{}", userId, resourceId, amount);
    }

    /***
     * 修改资源为元气值下载
     * @param userId
     * @param resourceId
     * @param amount
     */
    @Transactional
    public void changePayCoinAmount(String userId, String resourceId, Integer amount) {
        updatePayType(userId, resourceId, ResourcePayTypeEnum.COIN.getType(), amount);
        log.info("{} 修改资源{}为元气值下载资源，下载金额：{}", userId, resourceId, amount);
    }

    /***
     * 修改资源为免费下载
     * @param userId
     * @param resourceId
     */
    @Transactional
    public void changePayFreeAmount(String userId, String resourceId) {
        updatePayType(userId, resourceId, ResourcePayTypeEnum.FREE.getType(), 0);
        log.info("{} 修改资源{}为免费下载资源", userId, resourceId);
    }

    @Transactional
    public void updatePayType(String userId, String resourceId, Integer payType, Integer amount) {
        Assert.isTrue(userServiceClient.isAdmin( userId), "没有修改权限");
        ResourceEntity resourceEntity = resourceDAO.findAllByResourceId(resourceId);
        Assert.isTrue(resourceEntity != null &&
                !CommonStatusEnum.DELETED.getStatusCode().equals(resourceEntity.getStatus()), "资源不存在或已被删除");
        resourceEntity.setPayType(payType);
        resourceEntity.setPayAmount(amount);
        resourceDAO.save(resourceEntity);
        // 资源更新、缓存缓存
        resourceCacheService.refreshResourceDetailCacheById(resourceEntity.getId());
    }

    /***
     * 查询预览数据
     * @param resourceId
     * @param currentUserId
     * @return
     */
    public ResourceViewResponseVO view(String resourceId, String currentUserId) {
        ResourceViewResponseVO view = resourceCacheService.getResourceViewDetailFromCache(resourceId, currentUserId);
        if (view == null || !enableView(view.getStatus(), view.getReviewStatus())) {
            throw new NotFoundException("资源不存在或已下线");
        }
        Long viewCount = resourceCacheService.increxResourceViewCountCache(resourceId);
        Long downloadCount = resourceCacheService.getResourceDownloadCountFromCache(resourceId);
        Long downloadUserCount = resourceCacheService.getDownloadUserCountFromCache(resourceId);
        if (StringUtils.isNotBlank(currentUserId)) {
            AbstractResourceBehaviorService collectService =
                    ResourceBehaviorFactory.getBehaviorService(ResourceBehaviorTypeEnum.COLLECT.getBehavior());
            Map<Long, Boolean> isCollectResult = collectService.getIsBehaviorFromCache(ResourceTypeEnum.RESOURCE.getType(),  Arrays.asList(view.getId()), currentUserId);
            view.setIsCollect(isCollectResult.get(view.getId()) != null ? isCollectResult.get(view.getId()) : false);
        }
        view.getResourceCount().setViewCount(viewCount);
        view.getResourceCount().setDownloadCount(downloadCount);
        view.getResourceCount().setDownloadUserCount(downloadUserCount);
        return view;
    }

    private boolean enableView(Integer status, Integer reviewStatus) {
        return CommonStatusEnum.PUBLISHED.getStatusCode().equals(status) && ReviewStatusEnum.isPass(reviewStatus);
    }

    /***
     * 资源提交成功，后置处理
     * @param isFirstSubmit 首次提交
     * @param resourceEntity
     */
    private void submitAfterProcess(boolean isFirstSubmit, ResourceEntity resourceEntity) {
        // 资源包（OFFICE、PDF）转为图片 - 用于预览 【不提供直接下载、统一先预览在下载】
        if (isFirstSubmit) {
            this.resourceConvertImagesAndUpdateEntity(resourceEntity);
        }
        if (resourceEntity.getCcId() != null) {
            resourceCacheService.addCTCNewResourceCache(resourceEntity.getCcId(), resourceEntity.getResourceId());
        }
        // 消息通知
        this.sendSubmitMessageNotify(isFirstSubmit, resourceEntity);
        // 资源更新、缓存缓存
        resourceCacheService.refreshResourceDetailCacheById(resourceEntity.getId());
    }

    /***
     *  资源转图片，资源转图片的工厂处理，预览页统一处理为已图片的方式预览
     */
    private void resourceConvertImagesAndUpdateEntity(ResourceEntity resourceEntity) {
        // 资源转图片工厂服务
        AbstractResourceConvertImageService abstractResourceConvertImageService =
                ResourceConvertImageFactory.getConvertService(resourceEntity.getFormat());
        // 转换网络资源为图片
        ResourceConvertImageResult resourceConvertImageResult =
                abstractResourceConvertImageService.convert(resourceEntity.getResourceUrl(), MAX_CONVERT_IMAGE_COUNT);
        if (resourceConvertImageResult != null && CollectionUtils.isNotEmpty(resourceConvertImageResult.getImages())) {
            resourceEntity.setImageCount(resourceConvertImageResult.getPageCount());
            resourceEntity.setImages(JsonUtil.toJsonString(resourceConvertImageResult.getImages()));
            if (StringUtils.isBlank(resourceEntity.getCoverImage())) {
                // 如果没有设置封面图，则取第一张图片
                resourceEntity.setCoverImage(resourceConvertImageResult.getImages().get(0));
            }
        } else {
            resourceEntity.setImageCount(0);
            resourceEntity.setImages(JsonUtil.toJsonString(Lists.newArrayList()));
        }
        resourceDAO.save(resourceEntity);
    }

    /***
     * 资源更新，消息通知
     * @param isFirstSubmit
     * @param resourceEntity
     */
    private void sendSubmitMessageNotify(boolean isFirstSubmit, ResourceEntity resourceEntity) {
        String message = "有人更新了资源信息："+resourceEntity.getResourceId()+"，请关注！";
        if (isFirstSubmit) {
            message = "有人提交了新资源信息："+resourceEntity.getResourceId()+"，赶紧去审核吧！";
        }
        // 有人提交了资源，消息通知管理员，赶紧去审核吧
        // 站内系统消息通知
        String finalMessage = message;
        Set<String> adminUserIds = roleService.getSuperAdministratorUserIds();
        if (CollectionUtils.isEmpty(adminUserIds)) {
            return;
        }
        adminUserIds.stream().forEach(admin -> {
            SystemMessageBuilder systemMessageBuilder = new SystemMessageBuilder();
            SystemMessageDTO systemMessage = systemMessageBuilder.build(MessageConstants.TypeEnum.REVIEW_TASK_NOTIFY,
                    admin, finalMessage);
            messageQueue.submit(systemMessage);
        });
    }

    /***
     * 提交资源前置执行
     * @param resourceSaveOrUpdateVO
     * @return
     */
    public ResourceEntity submitBeforeProcess(ResourceSaveOrUpdateVO resourceSaveOrUpdateVO) {
        // 初始化资源信息
        resourceSaveOrUpdateVO.initResourceFileInfo();
        // 提交数据核验
        resourceSaveOrUpdateVO.validate();
        // 资源作者
        String author = resourceSaveOrUpdateVO.getAuthor();
        // 获取资源ID
        String resourceId = resourceSaveOrUpdateVO.getResourceId();
        // 是否首次提交
        boolean isFirstSubmit = StringUtils.isBlank(resourceId);
        ResourceEntity resourceEntity;
        if (isFirstSubmit) {
            // 生成资源业务ID
            resourceId = IDGenerateUtil.createId(ResourceTypeEnum.RESOURCE.getType());
            // 首次提交
            resourceEntity = new ResourceEntity();
            // 设置资源业务ID
            resourceEntity.setResourceId(resourceId);
            // 首次创建默认新建
            resourceEntity.setStatus(CommonStatusEnum.PUBLISHED.getStatusCode());
            // 首次创建默认待审核
            resourceEntity.setReviewStatus(ReviewStatusEnum.WAITE_REVIEW.getStatusCode());
            // 资源不支持刷新
            resourceEntity.setResourceUrl(resourceSaveOrUpdateVO.getResourceUrl());
            resourceEntity.setAuthor(author);
            resourceEntity.setHotVal(0);
            resourceEntity.setIsOfficial(false);
            resourceEntity.setIsBoutique(false);
            resourceEntity.setFormat(resourceSaveOrUpdateVO.getFormat());
            resourceEntity.setFileName(resourceSaveOrUpdateVO.getFileName());
            resourceEntity.setExtName(resourceSaveOrUpdateVO.getExtName());
            resourceEntity.setSize(resourceSaveOrUpdateVO.getSize());
        } else {
            // 更新资源
            resourceEntity = resourceDAO.findAllByResourceId(resourceId);
            // 校验资源
            Assert.isTrue(resourceEntity != null &&
                    !CommonStatusEnum.DELETED.getStatusCode().equals(resourceEntity.getStatus()), "资源不存在或已经删除");
            // 校验用户权限
            Assert.isTrue(resourceEntity.getAuthor().equals(author) || userServiceClient.isAdmin(author), "无权限");
        }
        // 更新资源数据
        resourceEntity.setUpdateBy(resourceSaveOrUpdateVO.getAuthor());
        resourceEntity.setResourceName(resourceSaveOrUpdateVO.getResourceName());
        resourceEntity.setPath(resourceSaveOrUpdateVO.getPath());
        resourceEntity.setCoverImage(resourceSaveOrUpdateVO.getCoverImage());
        resourceEntity.setDescription(resourceSaveOrUpdateVO.getDescription());
        resourceEntity.setContent(resourceSaveOrUpdateVO.getContent());
        resourceEntity.setPayType(resourceSaveOrUpdateVO.getPayType());
        resourceEntity.setPayAmount(resourceSaveOrUpdateVO.getPayAmount());
        resourceEntity.setCloudUrl(resourceSaveOrUpdateVO.getCloudUrl());
        resourceEntity.setCloudCode(resourceSaveOrUpdateVO.getCloudCode());
        resourceEntity.setCcId(resourceSaveOrUpdateVO.getCcId());
        return resourceDAO.save(resourceEntity);
    }

    /***
     * 资源列表查询
     * @return
     */
    public PAIPageResponseBeanUtil<ResourceDetailResponseVO> pageList(String resourceId,
                                                                      String author,
                                                                      String path,
                                                                      String format,
                                                                      String keyword,
                                                                      Integer status,
                                                                      Integer reviewStatus,
                                                                      Integer pageNo,
                                                                      Integer pageSize) {
        // 创建时间倒序
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Order.desc("id")));
        // 构造查询条件
        Specification<ResourceEntity> specification = buildResourceQueryCondition(resourceId, author, path, format, keyword, status, reviewStatus);
        Page<ResourceEntity> resourceEntities = resourceDAO.findAll(specification, pageable);
        List<ResourceDetailResponseVO> resourceDetailResponseVOList = new ArrayList<>();
        if (resourceEntities != null && resourceEntities.getTotalElements() > 0) {
            resourceDetailResponseVOList = ResourceEntityConvert.convertList(resourceEntities.getContent());
            // 扩展资源数据信息
            extResourceCountInfo(resourceDetailResponseVOList);
            // 扩展用户信息
            extResourceAuthorInfo(resourceDetailResponseVOList);
        }
        return PAIPageResponseBeanUtil.success(pageNo, pageSize, resourceEntities.getTotalElements(), resourceDetailResponseVOList);
    }


    /***
     * 我的提交列表
     * @param userId
     * @param pageNo
     * @param pageSize
     * @return
     */
    public PAIPageResponseBeanUtil<ResourceViewResponseVO> submitPageList(String userId, Integer pageNo, Integer pageSize) {
        // 创建时间倒序
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Order.desc("id")));
        Page<ResourceEntity> resourceEntities = resourceDAO.findAllByAuthorAndStatus(userId, CommonStatusEnum.PUBLISHED.getStatusCode(), pageable);
        List<ResourceBaseResponseVO> resourceBaseResponseVOList = new ArrayList<>();
        if (resourceEntities != null && resourceEntities.getTotalElements() > 0) {
            resourceBaseResponseVOList = ResourceEntityConvert.detail2BaseList(resourceEntities.getContent());
        }
        return PAIPageResponseBeanUtil.success(pageNo, pageSize, resourceEntities.getTotalElements(), resourceBaseResponseVOList);
    }

    /***
     * 下载列表
     * @param userId
     * @param pageNo
     * @param pageSize
     * @return
     */
    public PAIPageResponseBeanUtil<ResourceViewResponseVO> downloadPageList(String userId, Integer pageNo, Integer pageSize) {
        // 创建时间倒序
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Order.desc("id")));
        Page<ResourceDownloadEntity> resourceDownloadEntities;
        if (StringUtils.isBlank(userId)) {
            resourceDownloadEntities = resourceDownloadDAO.findAll(pageable);
        } else {
            resourceDownloadEntities = resourceDownloadDAO.findAllByUserId(userId, pageable);
        }
        List<ResourceBaseResponseVO> resourceBaseResponseVOList = new ArrayList<>();
        if (resourceDownloadEntities != null && resourceDownloadEntities.getTotalElements() > 0) {
            List<String> resourceIdList = resourceDownloadEntities.getContent().stream().map(ResourceDownloadEntity::getResourceId).collect(Collectors.toList());
            List<String> userIdList =
                    resourceDownloadEntities.getContent().stream().map(ResourceDownloadEntity::getUserId).collect(Collectors.toList());
            List<ResourceEntity> resourceEntities = resourceDAO.findAllByResourceIdIn(resourceIdList);
            Map<String, ResourceEntity> resourceMap =
                    resourceEntities.stream().collect(Collectors.toMap(ResourceEntity::getResourceId, ResourceEntity -> ResourceEntity));
            Map<String, UserBaseResponseInfoVO> userBaseResponseInfoVOMap =
                    userServiceClient.batchGetUserInfoFromCache(userIdList);
            for (ResourceDownloadEntity resourceDownloadEntity : resourceDownloadEntities.getContent()) {
                ResourceDownloadResponseVO downloadResponseVO = new ResourceDownloadResponseVO();
                ResourceDetailResponseVO vo =
                        ResourceEntityConvert.convert(resourceMap.get(resourceDownloadEntity.getResourceId()));
                BeanUtils.copyProperties(vo, downloadResponseVO);
                downloadResponseVO.setDownloadUser(userBaseResponseInfoVOMap.get(resourceDownloadEntity.getUserId()));
                downloadResponseVO.setDownloadTime(resourceDownloadEntity.getDownloadDate().getTime());
                resourceBaseResponseVOList.add(downloadResponseVO);
            }
        }
        return PAIPageResponseBeanUtil.success(pageNo, pageSize, resourceDownloadEntities.getTotalElements(), resourceBaseResponseVOList);
    }

    /***
     * 操作列表
     * @param userId
     * @param behaviorType
     * @param pageNo
     * @param pageSize
     * @return
     */
    public PAIPageResponseBeanUtil<ResourceViewResponseVO> behaviorPageList(String userId,
                                                                        String behaviorType,
                                                                        Integer pageNo,
                                                                        Integer pageSize) {
        AbstractResourceBehaviorService behaviorService = ResourceBehaviorFactory.getBehaviorService(behaviorType);
        Page<ResourceBehaviorEntity> resourceBehaviorEntities =
                behaviorService.page(userId, behaviorType, ResourceTypeEnum.RESOURCE.getType(), pageNo, pageSize);
        List<ResourceBaseResponseVO> resourceBaseResponseVOList = new ArrayList<>();
        if (resourceBehaviorEntities != null && resourceBehaviorEntities.getTotalElements() > 0) {
            List<Long> resourceIdList = resourceBehaviorEntities.getContent().stream().map(ResourceBehaviorEntity::getResourceIndex).collect(Collectors.toList());
            List<ResourceEntity> resourceEntities = resourceDAO.findAllById(resourceIdList);
            resourceBaseResponseVOList = ResourceEntityConvert.detail2BaseList(resourceEntities);
        }
        return PAIPageResponseBeanUtil.success(pageNo, pageSize, resourceBehaviorEntities.getTotalElements(), resourceBaseResponseVOList);
    }

    private void extResourceCountInfo(List<ResourceDetailResponseVO> resourceDetailResponseVOList) {
        if (org.springframework.util.CollectionUtils.isEmpty(resourceDetailResponseVOList)) {
            return;
        }
        resourceDetailResponseVOList.forEach(res -> {
            res.setResourceCount(new ResourceCountResponseVO());
        });
    }

    private void extResourceAuthorInfo(List<ResourceDetailResponseVO> resourceDetailResponseVOList) {
        if (org.springframework.util.CollectionUtils.isEmpty(resourceDetailResponseVOList)) {
            return;
        }
        List<String> suerIdList = resourceDetailResponseVOList.stream().map(ResourceDetailResponseVO::getAuthor).collect(Collectors.toList());
        List<String> reviews = resourceDetailResponseVOList.stream().map(ResourceDetailResponseVO::getReviewUserId).filter(uid -> StringUtils.isNotBlank(uid)).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(reviews)) {
            suerIdList.addAll(reviews);
        }
        Map<String, UserBaseResponseInfoVO> userInfoMap = userServiceClient.batchGetUserInfoFromCache(suerIdList);
        resourceDetailResponseVOList.forEach(res -> {
            res.setAuthorInfo(userInfoMap.get(res.getAuthor()));
            if (StringUtils.isNotBlank(res.getReviewUserId())) {
                res.setReviewManagerInfo(userInfoMap.get(res.getAuthor()));
            }
        });
    }

    /***
     * 构造瑞索斯列表查询条件
     * @param resourceId
     * @param author
     * @param path
     * @param format
     * @param keyword
     * @param status
     * @param reviewStatus
     * @return
     */

    private Specification<ResourceEntity> buildResourceQueryCondition(String resourceId,
                                                                      String author,
                                                                      String path,
                                                                      String format,
                                                                      String keyword,
                                                                      Integer status,
                                                                      Integer reviewStatus) {

        Specification<ResourceEntity> specification = new Specification<ResourceEntity>() {
            @Override
            public Predicate toPredicate(Root<ResourceEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                if (StringUtils.isNotBlank(resourceId)) {
                    predicates.add(criteriaBuilder.equal(root.get("resourceId").as(String.class), resourceId));
                }
                if (StringUtils.isNotBlank(author)) {
                    predicates.add(criteriaBuilder.equal(root.get("author").as(String.class), author));
                }
                if (StringUtils.isNotBlank(path)) {
                    predicates.add(criteriaBuilder.equal(root.get("path").as(String.class), path));
                }
                if (StringUtils.isNotBlank(format)) {
                    predicates.add(criteriaBuilder.equal(root.get("format").as(String.class), format));
                }
                if (status != null) {
                    predicates.add(criteriaBuilder.notEqual(root.get("status").as(Integer.class), CommonStatusEnum.DELETED.getStatusCode()));
                    predicates.add(criteriaBuilder.equal(root.get("status").as(Integer.class), status));
                }
                if (reviewStatus != null) {
                    predicates.add(criteriaBuilder.equal(root.get("reviewStatus").as(Integer.class), reviewStatus));
                }
                if (StringUtils.isNotBlank(keyword)) {
                    predicates.add(
                            criteriaBuilder.or(
                                    criteriaBuilder.like(root.get("resourceName").as(String.class), "%" + keyword + "%"),
                                    criteriaBuilder.like(root.get("fileName").as(String.class), "%" + keyword + "%"),
                                    criteriaBuilder.like(root.get("description").as(String.class), "%" + keyword + "%"),
                                    criteriaBuilder.like(root.get("content").as(String.class), "%" + keyword + "%")));
                }
                Predicate[] pre = new Predicate[predicates.size()];
                criteriaQuery.where(predicates.toArray(pre));
                return criteriaQuery.getRestriction();
            }
        };

        return specification;
    }

    /**
     * 审核之后的后置处理
     * @param resourceEntity
     */
    private void reviewAfterProcess(ResourceEntity resourceEntity) {
        /**
         * 刷新资源、榜单缓存
         */
        refreshResourceCache(resourceEntity);
        /**
         * 发送系统通知
         */
        String reviewMessage = null;
        if (resourceEntity.getReviewStatus() == ReviewStatusEnum.PASS_SCORE_1.getStatusCode() ||
                resourceEntity.getReviewStatus() == ReviewStatusEnum.PASS_SCORE_2.getStatusCode()) {
            reviewMessage = "你在瑞索斯提交的资源《"+resourceEntity.getResourceName()+"》审核通过，积分奖励请查收，再次感谢你的支持";
        } else if (resourceEntity.getReviewStatus() == ReviewStatusEnum.REFUSE_SYSTEM.getStatusCode() ||
                resourceEntity.getReviewStatus() == ReviewStatusEnum.REFUSE_USER.getStatusCode()) {
            reviewMessage = "抱歉，你在瑞索斯提交的资源《"+resourceEntity.getResourceName()+"》审核未通过，原因："+resourceEntity.getReviewReason();
        }
        if (StringUtils.isNotBlank(reviewMessage)) {
            SystemMessageBuilder systemMessageBuilder = new SystemMessageBuilder();
            SystemMessageDTO systemMessage = systemMessageBuilder.buildResourceMessage(MessageConstants.TypeEnum.REVIEW_RESULT_NOTIFY,
                                                                                       resourceEntity.getAuthor(),
                                                                                       reviewMessage,
                                                                                       ResourceTypeEnum.RESOURCE,
                                                                                       resourceEntity.getResourceId());
            messageQueue.submit(systemMessage);
        }
    }

    /***
     * 刷新资源缓存
     */
    private void refreshResourceCache(ResourceEntity resourceEntity) {
        // 刷新资源缓存
        resourceCacheService.refreshResourceDetailCacheByResourceEntity(resourceEntity);
        // 刷新实时最新榜单
        resourceListService.refreshRealTimeNewList(resourceEntity.getPath());
        if (resourceEntity.getCcId() != null) {
            resourceListService.refreshCtcRealTimeNewList(resourceEntity.getCcId(), resourceEntity.getResourceId(),
                    resourceEntity.getId());
        }
    }

    /***
     * 下载鉴权
     * @param resourceId
     * @param userId
     * @return
     */
    public Integer downloadAuth(String resourceId, String userId) {
        if (StringUtils.isBlank(userId)) {
            return PayAuthResultEnum.ERROR_UN_LOGIN.getCode();
        }
        ResourceDetailResponseVO resource = resourceCacheService.getResourceDetailDetailFromCache(resourceId);
        Assert.isTrue(resource != null, "资源不存在");
        Assert.isTrue(CommonStatusEnum.PUBLISHED.getStatusCode() == resource.getStatus() && ReviewStatusEnum.isPass(resource.getReviewStatus()), "资源状态异常");
        Integer payType = resource.getPayType();
        if (ResourcePayTypeEnum.isCoin(payType) || ResourcePayTypeEnum.isMoney(payType)) {
            if (!pathService.isSub(userId)) {
                return PayAuthResultEnum.UN_SUB.getCode();
            }
        }
        // 免费资源 或者符合下载条件
        return PayAuthResultEnum.OK.getCode();
    }

    /***
     * 下载资源
     * @param resourceId
     * @param userId
     */
    public void downloadResource(HttpServletResponse response, String resourceId, String userId) {
        // 安全考虑，下载接口也需要重新走一遍校验
        this.doubleCheckDownloadResourceAuth(resourceId, userId);
        // 获取资源信息
        ResourceDetailResponseVO resource = resourceCacheService.getResourceDetailDetailFromCache(resourceId);
        // 资源信息校验
        Assert.isTrue(resource != null, "资源不存在");
        Assert.isTrue(CommonStatusEnum.PUBLISHED.getStatusCode() == resource.getStatus() && ReviewStatusEnum.isPass(resource.getReviewStatus()), "资源状态异常");
        // 下载文件
        //通知浏览器以附件形式下载
        try {
            if (resource.getPath().equals(ResourcePathEnum.CLOUD.name())) {
                // 云盘资源下载，合成txt文件
                response.setHeader("Content-Disposition", "attachment;filename=" +
                        new String(resource.getResourceName().concat(".txt").getBytes(), "ISO-8859-1"));
                String cloudUrl = resource.getCloudUrl();
                String cloudCode = resource.getCloudCode();
                StringBuilder stringBuilder = new StringBuilder("\n"+resource.getResourceName() + "云盘资源下载：\n");
                stringBuilder.append("\n云盘链接:"+cloudUrl);
                stringBuilder.append("\n提取码:"+cloudCode);
                fileUploadService.createTxtAndDownload(response, stringBuilder.toString());
            } else {
                // 下载资源
                response.setHeader("Content-Disposition", "attachment;filename=" +
                        new String(getFileName(resource).getBytes(), "ISO-8859-1"));
                fileUploadService.download(response.getOutputStream(), resource.getResourceUrl());
            }
            // 下载完成后置流程处理
            ThreadPoolExecutorUtil.execute(() -> downloadAfterProcess(resource, resourceId, userId));
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("下载异常");
        }
    }

    private String getFileName(ResourceDetailResponseVO resource) {
        if (resource == null) {
            return null;
        }
        String name = resource.getResourceName();
        if (name.indexOf(".") > -1) {
            return name;
        }
        String url = resource.getResourceUrl();
        if (url.indexOf(".") == -1) {
            return name;
        }
        return name + url.substring(url.lastIndexOf("."));
    }

    private void doubleCheckDownloadResourceAuth(String resourceId, String userId) {
        int code = downloadAuth(resourceId, userId);
        if (code == PayAuthResultEnum.ERROR_UN_LOGIN.getCode()) {
            throw new BusinessException("未登录");
        } else if (code == PayAuthResultEnum.UN_SUB.getCode()) {
            throw new BusinessException("订阅后解锁");
        }
    }

    private void downloadAfterProcess(ResourceDetailResponseVO resource, String resourceId, String userId) {

        // 增加下载记录
        resourceCacheService.addDownloadRecordCache(resource.getCcId(), resourceId, userId);
        if (ResourcePayTypeEnum.isCoin(resource.getPayType())) {
            Integer coin = resource.getPayAmount();
            SystemMessageBuilder systemMessageBuilder = new SystemMessageBuilder();
            SystemMessageDTO systemMessage = systemMessageBuilder.buildResourceMessage(
                    MessageConstants.TypeEnum.INTEGRAL_UPDATE,
                    userId,
                    "下载瑞索斯资源《"+resource.getResourceName()+"》，消耗"+coin+"个猿气值",
                    ResourceTypeEnum.RESOURCE,
                    resourceId);
            messageQueue.submit(systemMessage);

            SystemMessageDTO systemMessage2 = systemMessageBuilder.buildResourceMessage(
                    MessageConstants.TypeEnum.INTEGRAL_UPDATE,
                    resource.getAuthor(),
                    "上传在瑞索斯的资源《"+resource.getResourceName()+"》，被下载了一次，赚了"+coin+"个猿气值",
                    ResourceTypeEnum.RESOURCE,
                    resourceId);
            messageQueue.submit(systemMessage2);
        }
        resourceCacheService.addDownloadUserSetCache(resourceId, userId);
        resourceCacheService.increxResourceDownloadCountCache(resourceId);
        // 下载记录
        resourceDownloadRecordService.log(resourceId, userId);
    }

    @Override
    public DataCountResponseVO monitorDataCount() {
        Long totalCount = resourceDAO.countOnlineResource();
        DataCountResponseVO dataCountResponseVO = new DataCountResponseVO();
        dataCountResponseVO.setTotalCount(totalCount);
        return dataCountResponseVO;
    }

    @Override
    public ResourceBaseInfoVO getResourceBaseInfo(Long resourceId) {
        return null;
    }

    /***
     * 同步ES，提供搜索能力
     * @param start
     * @param endDate
     * @return
     */
    @Override
    public List<DtoRequest> getSyncResourcesByDate(Date start, Date endDate) {
        List<ResourceEntity> resourceEntities = resourceDAO.findAllByUpdateDateBetween(start, endDate);
        return EsSyncResourceConvert.resourceConvert2EsDTOList(resourceEntities);
    }

    @Override
    public Map<String, MessageResourceVO> batchGetMessResource(List<String> resourceIdList) {
        Map<String, MessageResourceVO> map = new HashMap<>();
        Map<String, ResourceDetailResponseVO> resourceResponseVOMap = resourceCacheService.batchGetResourceDetailFromCache(resourceIdList, false, false);
        for (String resourceId : resourceIdList) {
            ResourceDetailResponseVO resourceDetailResponse = resourceResponseVOMap.get(resourceId);
            if (resourceDetailResponse != null) {
                MessageResourceVO messageResource = new MessageResourceVO();
                messageResource.setId(resourceId);
                messageResource.setTitle(resourceDetailResponse.getResourceName());
                messageResource.setCover(resourceDetailResponse.getCoverImage());
                messageResource.setAuthor(resourceDetailResponse.getAuthor());
                messageResource.setModule(ResourceTypeEnum.RESOURCE.getTypeName());
                messageResource.setType(ResourceTypeEnum.RESOURCE.getType());
                map.put(resourceId, messageResource);
            }
        }
        return map;
    }

    /**
     * 查询资源数量
     * @param ccId
     * @return
     */
    public Long getCTCResourceCountFromCache(Long ccId) {
        return resourceDAO.countCtcOnlineResource(ccId);
    }

    public Long getCTCVideoCountFromCache(Long ccId) {
        return videoDAO.countCtcVideo(ccId);
    }
}
